package org.github.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import java.time.LocalDateTime;
import java.util.List;
import lombok.val;
import org.apache.ibatis.reflection.MetaObject;
import org.github.core.base.Entity;
import org.github.core.mybatis.MyBatisMapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static com.google.common.base.MoreObjects.firstNonNull;
import static org.github.core.Const.CREATED_BY;
import static org.github.core.Const.CREATED_ON;
import static org.github.core.Const.UPDATED_BY;
import static org.github.core.Const.UPDATED_ON;

@MapperScan(value = "org.github", annotationClass = MyBatisMapper.class)
@Configuration
public class MyBatisConfig {
  @Bean
  public CustomIdentifierGenerator customIdentifierGenerator() {
    return new CustomIdentifierGenerator();
  }

  @Bean
  public CustomMetaObjectHandler customMetaObjectHandler() {
    return new CustomMetaObjectHandler();
  }

  @Bean
  public CustomSqlInjector customSqlInjector() {
    return new CustomSqlInjector();
  }

  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    //interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
    interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
    return interceptor;
  }

  private static class CustomSqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
      val methods = super.getMethodList(mapperClass, tableInfo);
      methods.add(new InsertBatchSomeColumn());
      return methods;
    }
  }

  private static class CustomMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject entity) {
      strictInsertFill(entity, CREATED_BY, "system"::toString, String.class);
      strictInsertFill(entity, CREATED_ON, LocalDateTime::now, LocalDateTime.class);
      strictInsertFill(entity, UPDATED_BY, "system"::toString, String.class);
      strictInsertFill(entity, UPDATED_ON, LocalDateTime::now, LocalDateTime.class);
    }

    @Override
    public void updateFill(MetaObject entity) {
      strictUpdateFill(entity, "updatedBy", "system"::toString, String.class);
      strictUpdateFill(entity, "updatedAt", LocalDateTime::now, LocalDateTime.class);
    }
  }

  @SuppressWarnings("deprecation")
  private static class CustomIdentifierGenerator extends DefaultIdentifierGenerator {
    @Override
    public Long nextId(Object entity) {
      if (!(entity instanceof Entity)) return super.nextId(entity);
      val pk = ((Long) ((Entity) entity).pkVal());
      return firstNonNull(pk, super.nextId(entity));
    }

    @Override
    public String nextUUID(Object entity) {
      if (!(entity instanceof Entity)) return super.nextUUID(entity);
      val pk = ((String) ((Entity) entity).pkVal());
      return firstNonNull(pk, super.nextUUID(entity));
    }
  }
}
